iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0

什麼是Compound Component?

我們來看一下,React 最一開始教大家用 props 寫共用 component 的方法


// with props 

const product: Product = {
  id: 1,
  name: 'product',
  title: 'product',
  price: 99,
  category: 'product',
  rating: 4,
  image: 'https://picsum.photos/seed/1/640/480',
  discount: 0.2,
  description: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quibusdam.'
}

<ProductCard product = {product} />
// ProductCard

function ProductCard({ product }: Props) {
  return (
    <div className={styles.productCard}>
      <div className={styles.imageContainer} >
        <img className={styles.image} src={product.image} alt={product.title} />
      </div>
      <div className={styles.info}>{product.name}</div>
      <div className={styles.description}>{product.description}</div>
      <div className={styles.buttonContainer}>
        <div className={styles.button}> add to cart </div>
      </div>
    </div>
  );
}

https://ithelp.ithome.com.tw/upload/images/20230921/20161792NfkK6gPhZB.png

上述這樣做法會有一些小問題要處理

  1. 如果我今天要隱藏 button 的話我就必須在ProductCard的 component 加入判斷式
  2. 如果我今天要調換 component 內部的順序就要在重構另外一種 Card 了
function ProductCard({ product , showButton }: Props) {
  return (
    <div className={styles.productCard}>
      <div className={styles.imageContainer} >
        <img className={styles.image} src={product.image} alt={product.title} />
      </div>
      <div className={styles.info}>{product.name}</div>
      <div className={styles.description}>{product.description}</div>
      {
			showButton &&
			<div className={styles.buttonContainer}>
                <div className={styles.button}> add to cart </div>
            </div>
        }
    </div>
  );
}

當然也可以將每個部分再切成元件之後再放入 ProductCard 裡面,components 整理過後可能會是長這樣

function ProductCard({ product , showButton }: Props) {
  return (
		
    <div className={styles.productCard}>
			<ProdcutImage image = {product.image}/>
			<ProductName info = {product.info}/>
			<ProdcutDescription description = {product.description}/>
	    {showButton && <Button />}
    </div>
  );
}

為了解決剛剛列出的問題,也讓我們的 component 更好維護,就可以使用 Compound Component。

要使用 compound component 我們會用到 useContext,透過 useContext 把本來用 props 的方式改成利用provider 給資料

//ProductCardContext

import * as React from 'react';
import { createContext, useContext } from 'react';
import { Product } from '../types';

const ProductCardContext = createContext<{ product: Product } | null>(null);

export function useProductCardContext() {
  const context = useContext(ProductCardContext);
  if (!context) {
    throw new Error(
      'ProductCard.* component must be rendered as child of ProductCard component'
    );
  }
  return context;
}

export default ProductCardContext;
import { ReactNode } from 'react';
import * as React from 'react';
import ProductCardContext from './ProductCardContext';
import { Product } from '../types';
import ProductImage from './ProductImage';
import ProductButton from './ProductButton';
import ProductTitle from './ProductTitle';
import ProductInfo from './ProductInfo';
import ProductCategory from './ProductCategory';
import ProductRating from './ProductRating';
import ProductPrice from './ProductPrice';

type Props = {
  product: Product;
  image?: ReactNode;
  info?: ReactNode;
  action?: ReactNode;
};

function ProductCard({ image, info, action, product }: Props) {
  return (
    <ProductCardContext.Provider value={{ product }}>
      <div className="product-card">
        {image}
        <div className="product-card-bottom">
          {info}
          {action}
        </div>
      </div>
    </ProductCardContext.Provider>
  );
}

ProductCard.Image = ProductImage;
ProductCard.Button = ProductButton;
ProductCard.Title = ProductTitle;
ProductCard.Info = ProductInfo;
ProductCard.Category = ProductCategory;
ProductCard.Rating = ProductRating;
ProductCard.Price = ProductPrice;

export default ProductCard;

最後在使用 ProductCard的時候,就可以依自己的需求加入元件。再修復bug的時候也可以看哪裡出問題對症下藥

<ProductCard
      product={product}
      image={<ProductCard.Image />}
      info={
        <ProductCard.Info>
          <ProductCard.Category />
          <ProductCard.Title />
          <ProductCard.Rating />
          <ProductCard.Price />
        </ProductCard.Info>
      }
      action={
        <ProductCard.Button onClick={addToCart}>Add to cart</ProductCard.Button>
      }
   />

上一篇
Day 19 - 可以試著用在專案的 Functional Programing 觀念 part 2
下一篇
Day 21 - React Clean Code 以前沒注意到的小錯誤
系列文
React Clean Code And Unit Tests - 利用測試寫出人類看得懂的React程式30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言